#-*- coding:utf-8 -*-

from   flask     import *
import threading as     td
import socket    as     line
import requests
import string
import ast
import os
import random
import time

__verson__ = '1.9.4'

app = Flask(__name__)

def GetDiskList():
    disk_list = []
    for c in string.ascii_uppercase:
        disk = c+':'
        if os.path.isdir(disk):
            disk_list.append(disk)
    if os.path.isdir('/'):
        disk_list.append(disk)
    return disk_list

def ToIP(domain):
    addr = line.getaddrinfo(domain, 'http')
    return addr[0][4][0]

def HostIP():
    try:
        s = line.socket(line.AF_INET, line.SOCK_DGRAM)
        s.connect(('8.8.8.8', 80))
        ip = s.getsockname()[0]
    finally:
        s.close()
    return ip

def recv(clint, head):
    size = 0
    sizeValue = int(head['size'])
    with open(head['path'], 'wb') as file:
        while size < sizeValue:
            value = sizeValue - size
            if value > 1024:
                getdate = clint.recv(1024)
            else :
                getdate = clint.recv(value)
            file.write(getdate)
            size += 1024

def send(clint, path):
    daze = os.path.getsize(path)
    size = 0
    with open(path, 'rb') as file:
        while size < daze:
            fileDate = file.read(1024)
            clint.send(fileDate)
            size += 1024

def listdir(path):
    if os.path.exists(path) == True:
        dat = {"files":[],"folders":[],"unknows":[]}
        for i in os.listdir(path):
            if os.path.isdir(path + i) == True:
                dat["folders"].append(i)
            elif os.path.isfile(path + i) == True:
                dat["files"].append(i)
            else:
                dat["unknows"].append(i)
        return dat

class Solution:
    def simplifyPath(self, path):
        path.replace('//', '/')
        list = path.split('/')
        res =[]
        for i in range(len(list)):
            if list[i] == '..' and len(res) > 0:
                res = res[:-1]
            elif list[i] != '' and list[i] != '.' and list[i] != '..':
                res.append(list[i])
        return '/'+'/'.join(res)
    
    def subdirectories(self, path):
        pat = self.simplifyPath(path[0]).split('/')
        pah = self.simplifyPath(path[1]).split('/')
        n = 0
        res = []
        for p in pat:
            if n + 1 > len(pah):
                return {'target':'top','path':'/'.join(res)}
            if p == pah[n]:
                res.append(pah[n])
            else:
                return {'target':'top','path':'/'.join(res)}
            n = n + 1
        if n + 1 == len(pat):
            return {'target':'true','path':'/'+'/'.join(res)}
        else:
            for r in res:
                pah.remove(r)
            return {'target':'true','path':'/'+'/'.join(pah)}
    
    def window_capture(self, filename):
        pass

class Core():
    def __init__(self):
        try:rs = requests.get("http://127.0.0.1:10305/net/info/").text
        except:rs = "{'port':10306}"
        port = ast.literal_eval(rs)
        self.token = ''.join(random.sample(string.ascii_letters + string.digits, 8))
        self.sotion = Solution()
        self.connumber = {}
        self.nectnumber = {}
        self.__port = port['port']
        self.__password = None
        self.__ser = line.socket(line.AF_INET, line.SOCK_STREAM)
        self.__ser.bind((HostIP(),self.__port))
        self.__TCP = td.Thread(target=self.__TCP_Link)
        self.__TCP.start()

    def __Options(self, ip, data):
        get = self.nectnumber[ip]['line']
        c = data['command']
        if c == 'send_file':
            if os.path.isfile(data['path']) == True:
                size = os.path.getsize(data['path'])
                head = {
                    'size':size
                }
                get.send(str(head).encode('utf-8'))
                send(get, data['path'])
            else:
                get.send('PathError'.encode('utf-8'))
        if c == 'recv_file':
            head = ast.literal_eval(get.recv(1024).decode("utf-8"))
            dirname, filename = os.path.split(head['path'])
            if os.path.exists(dirname) == True:
                get.send('True'.encode('utf-8'))
                recv(get, head)
            else:
                get.send('PathError'.encode('utf-8'))
        if c == 'disk':
            dir = GetDiskList()
            get.send(str(dir).encode('utf-8'))
        if c == 'list_file':
            try:
                dir = listdir(data['path'])
                get.send(str(dir).encode('utf-8'))
            except:
                get.send('PathError'.encode('utf-8'))
        if c == 'star_path':
            pass
        ''''''
        if c == 'info':
            if data['info'] == 'password':
                if data['password'] == self.__password:
                    get.send('True'.encode('utf-8'))
                elif data['password'] == '' and self.__password == None:
                    get.send('True'.encode('utf-8'))
                else:
                    get.send('False'.encode('utf-8'))
            elif data['info'] == 'terminal':
                res = {
                    'con':self.connumber,
                    'nect':self.nectnumber
                }
                get.send(str(res).encode('utf-8'))
            elif data['info'] == 'capture':
                self.sotion.window_capture('.\\as.png')
            else:
                get.send('Unknow'.encode('utf-8'))
        ''''''
        if c == 'shell':
            cmd = c['todo']
            print(cmd)
            get.send('Unknow'.encode('utf-8'))
        if c == 'desktion':
            pass
        if c == 'operate':
            pass


    def __TCP_Connection_response(self, ip):
        get = self.nectnumber[ip]['line']
        while True:
            try:
                data = get.recv(128).decode("utf-8")
                if data == 'disconnect' or data == '':
                   del self.nectnumber[ip]
                   break
                data = ast.literal_eval(data)
                self.__Options(ip, data)
            except:
                break
        get.close()
    
    def __TCP_Link(self):
        self.__ser.listen(10)
        while True:
            cli, addr = self.__ser.accept()
            token = cli.recv(8).decode('utf-8')
            rdt = Get_info()
            rdt['token'] = self.token
            if self.__password == None:
                cli.send(str(rdt).encode("utf-8"))
            else:
                cli.send("Password".encode("utf-8"))
                data = cli.recv(128).decode('utf-8')
                if data == self.__password:
                    cli.send(str(rdt).encode("utf8"))
                else:
                    cli.close()
                    continue
            ip = addr[0]
            data = cli.recv(128).decode('utf-8')
            try:
                addr = ast.literal_eval(data)
            except:
                cli.close()
                continue
            self.nectnumber[token] = {
                'name':addr['name'],
                'ip':ip,
                'verson':addr['verson'],
                'line':cli
            }
            t = td.Thread(target=self.__TCP_Connection_response, args=(ip,))
            t.start()
    
    def Get_info(self):
        try:rs = requests.get("http://127.0.0.1:10305/net/info/").text
        except:
            return {
            "name":line.gethostname(),
            "ip":HostIP(),
            "port":self.__port,
            "verson":__verson__,
            }
        rs = ast.literal_eval(rs)
        return rs

    def Connect(self, addr, command=None):
        ip = addr['ip']
        port = int(addr['port'])
        cli = line.socket(line.AF_INET, line.SOCK_STREAM)
        try:cli.connect((ip,port))
        except:return 'ConnectError'
        cli.send(self.token.encode("utf-8"))
        data = cli.recv(128)
        data = data.decode('utf-8')
        if data == 'Password':
            cli.send(command.encode("utf-8"))
            data = cli.recv(128)
            data = data.decode('utf-8')
            if not '{' in data:
                cli.close()
                return 'PasswordError'
        data = ast.literal_eval(data)
        cli.send(str(self.Get_info()).encode("utf-8"))
        self.connumber[data['token']] = {
                    'name':data['name'],
                    'ip':ip,
                    'verson':data['verson'],
                    'line':cli
                }
        return data['token']
    
    def Tar(self, token):
        try:tar = self.connumber[token]['line']
        except:return 'IPError'
        return tar
    
    def Disconnect(self, ip):
        tar = self.Tar(ip)
        if tar == 'IPError':return 'IPError'
        tar.send('disconnect'.encode('utf-8'))
        tar.close()
        del self.connumber[ip]
        return True

    def Dir(self, ip, cmd, path='', target=''):
        tar = self.Tar(ip)
        if tar == 'IPError':return 'IPError'
        dat = {
            'command':cmd,
            'path':path,
            'target':target
        }
        tar.send(str(dat).encode('utf-8'))
        data = tar.recv(2048).decode('utf-8')
        if not cmd in ['list_file','disk','send_file']: return data
        if 'Error' in data:return data
        data = ast.literal_eval(data)
        return data
    
    def GetFile(self, ip, path, local='.\\'):
        if os.path.exists(local) != True:return 'localPathError'
        tar = self.Tar(ip)
        if tar == 'IPError':return 'IPError'
        res = self.Dir(ip, 'send_file', path)
        dirname, filename = os.path.split(path)
        res['path'] = local + filename
        recv(tar, res)
        return 'True'
    
    def SendFile(self, ip, local, path):
        if os.path.isfile(local) != True:return 'localPathError'
        tar = self.Tar(ip)
        if tar == 'IPError': return 'IPError'
        dat = {
            'command':'recv_file'
        }
        tar.send(str(dat).encode('utf-8'))
        size = os.path.getsize(local)
        tem, filename = os.path.split(local)
        dirname, tem = os.path.split(path)
        head = {
            'size':size,
            'path':dirname + filename
        }
        tar.send(str(head).encode('utf-8'))
        sta = tar.recv(128).decode('utf-8')
        if sta == 'PathError':return 'cloudPathError'
        send(tar, local)
        return 'True'

    def Rename(self, ip,):
        tar = self.Tar(ip)
        if tar == 'IPError': return 'IPError'
        pass

    def InfoDict(self, tar, info):
        tar.send(str(info).encode('utf-8'))
        res = tar.recv(128).decode('utf-8')
        return res

    def CheckPwd(self, ip, password):
        tar = self.Tar(ip)
        if tar == 'IPError': return 'IPError'
        info = {
            'command':'info',
            'info':'password',
            'password':password
        }
        res = self.InfoDict(tar, info)
        if res == 'True':return True
        else:return False
    
    def Remote_Capture(self, ip):
        tar = self.Tar(ip)
        if tar == 'IPError': return 'IPError'
        info = {
            'command':'info',
            'info':'capture'
        }
        res = self.InfoDict(tar, info)
        return self.GetFile(ip, '.\\as.png', 'E:\\')

    def Remote_Terminal(self, ip):
        tar = self.Tar(ip)
        if tar == 'IPError': return 'IPError'
        info = {
            'command':'info',
            'info':'terminal'
        }
        res = self.InfoDict(tar, info)
        res = ast.literal_eval(res)
        return res

    def Remote_Operation(self, ip, command):
        tar = self.Tar(ip)
        if tar == 'IPError': return 'IPError'
        info = {
            'command':'shell',
            'todo':command
        }
        res = self.InfoDict(tar, info)
        return res

N = Core()
app = Flask(__name__)

@app.route("/terminal/<req>/", methods=['POST','GET'])
def terminal(req='con'):
    ip = request.remote_addr
    if ip != '127.0.0.1':
        if ip != HostIP():return
    rs = {}
    if req=='con':
        for i in N.connumber:
            rs[i] = {'name':N.connumber[i]['name'],'verson':N.connumber[i]['verson']}
    if req=='net':
        for i in N.nectnumber:
            rs[i] = {'name':N.connumber[i]['name'],'verson':N.connumber[i]['verson']}
    return rs

@app.route("/connect/<addr>/<pwd>/", methods=['POST','GET'])
@app.route("/connect/<addr>/", methods=['POST','GET'])
def connect(addr, pwd=None):
    ip = request.remote_addr
    if ip != '127.0.0.1':
        if ip != HostIP():return
    rs = N.Connect(ast.literal_eval(addr), pwd)
    return rs

@app.route("/disconnect/<ip>/")
def disconnect(ip):
    ip = request.remote_addr
    if ip != '127.0.0.1':
        if ip != HostIP():return
    rs = N.Disconnect(ip)
    return rs

@app.route("/dir/<cmd>/<ip>/<path>/<local>/")
@app.route("/dir/<cmd>/<ip>/<path>/")
@app.route("/dir/<cmd>/<ip>/")
def dir(cmd, ip, path=None, local=None):
    if cmd == 'disk':
        return str(N.Dir(ip, 'disk'))
    if cmd == 'list':
        return str(N.Dir(ip, 'list_file', path=path.replace(':-', '/')))
    if cmd == 'get':
        return str(N.GetFile(ip, path.replace(':-', '/'), local.replace(':-', '/')))
    if cmd == 'send':
        return str(N.SendFile(ip, local.replace(':-', '/'), path.replace(':-', '/')))
    if cmd == 'rename':
        N.rename()

if __name__ == "__main__":
    app.run(host="127.0.0.1", port=10306)